package edu.towson.cosc.cosc455.kkarolenko.markdowncompiler;

import java.util.ArrayList;
import java.util.Arrays;

import edu.towson.cosc.cosc455.interfaces.LexicalAnalyzer;

// Analyze a file in the form of a string and export tokens
public class MLexicalAnalyzer implements LexicalAnalyzer {

	private String currentToken = "";
	private String nextCharacter = "";
	private int currentPosition = 0;
	private ArrayList<Token> validTokens = new ArrayList<Token>();
	private Token[] allTokens = {new Token("#START DOCUMENT", "SD", "<html>"), new Token("#END DOCUMENT", "ED", "</html>"),
								 new Token("#START HEAD", "SH", "<head>"), new Token("#END HEAD", "EH", "</head>"),
								 new Token("#START TITLE", "ST", "<title>"), new Token("#END TITLE", "ET", "</title>"),
								 new Token("#START PARAGRAPH", "SP", "<p>"), new Token("#END PARAGRAPH", "EP", "</p>"),
								 new Token("#START BOLD", "SB", "<b>"), new Token("#END BOLD", "EB", "</b>"),
								 new Token("#START ITALICS", "SI", "<i>"), new Token("#END ITALICS", "EI", "</i>"),
								 new Token("#START LIST", "SL", "<ul>"), new Token("#END LIST", "EL", "</ul>"),
								 new Token("#START ITEM", "SIT", "<li>"), new Token("#END ITEM", "EIT", "</li>"),
								 new Token("#START AUDIO", "SA", "<audio>"), new Token("#END AUDIO", "EA", "</audio>"),
								 new Token("#START VIDEO", "SV", ""), new Token("#END VIDEO", "EV", ""),
								 new Token("#START DEFINE", "SDF"), new Token("#END DEFINE", "EDF"),
								 new Token("#VALUE", "VAL"), new Token("#NAME", "NAME"),
								 new Token("#START USE", "SU"), new Token("#END USE", "EU"),
								 new Token("#NEWLINE", "NL", "</br>")};
	        
	// add all the tokens to the arraylist on construction for easy lookup
	public MLexicalAnalyzer() {
		validTokens.addAll(Arrays.asList(allTokens));
	}
	
	// if this is a newline, tab, or carriage return
	public boolean isSkipChar(String s) {
		if (isSpace(s) || s.equals("\n") || s.equals("\t") || s.equals("\r")) {
			return true;
		}
		return false;
	}
	
	/**
	 * This is the public method to be called when the Syntax Analyzer needs a new
	 * token to be parsed.
	 * @throws CompilerException 
	 */
	@Override
	public void getNextToken() throws CompilerException {
		
		// While it is a space, \n, or \t, get the next character
		while (isSkipChar(nextCharacter)) {
			getCharacter();
		}
		// If token
		if (nextCharacter.equals("#")) {
			
			addCharacter();
			// NEWLINE, NAME, or VALUE case
			if (nextCharacter.equalsIgnoreCase("N") || nextCharacter.equalsIgnoreCase("V")) {
				addCharacter();
				
				// until skip char, add chars
				while (!isSkipChar(nextCharacter)) {
					addCharacter();
				}
				
				// is this valid?
				lookupToken();
			}
			// START/END case
			else if (nextCharacter.equalsIgnoreCase("S") || nextCharacter.equalsIgnoreCase("E")) {
				// Get the first half of the token
				while (!isSpace(nextCharacter)) {
					addCharacter();
				}
				// Ignore extra spaces
				addCharacter();
				
				// skip more spaces
				while(isSpace(nextCharacter)) {
					getCharacter();
				}
				// Get second half of the token
				while(!isSkipChar(nextCharacter)) {
					addCharacter();
				}
				
				// valid?
				lookupToken();
			}
			// Illegal token
			else {
				// error
				throw new CompilerException("Lexical Error: Invalid Token: " + currentToken + nextCharacter);
			}
		}
		
		// If text
		else {
			while (!nextCharacter.equals("#")) {
				addCharacter();
			}
			Compiler.tokens.add(new Token(currentToken, "TEXT", currentToken));
			currentToken = "";
		}
	}

	/**
	 * This is method gets the next character from the input and places it in
	 * the nextCharacter class variable.
	 *
	 * @return the character
	 */
	@Override
	public boolean getCharacter() {
		if (moreInFile()) {
			// Get the next character and turn it into a string
			nextCharacter = Character.toString(Compiler.file.charAt(currentPosition));
			currentPosition++;
			return true;
		}
		return false;	
	}

	/**
     * This method adds the current character the nextToken.
     */
	@Override
	public void addCharacter() {
		// TODO Auto-generated method stub
		currentToken += nextCharacter;
		getCharacter();
	}

	/**
	 * This is method gets the next character from the input and places it in
	 * the nextCharacter class variable.
	 *
	 * @param c the current character
	 * @return true, if is space; otherwise false
	 */
	@Override
	public boolean isSpace(String s) {
		if (s.equals(" ")) {
			return true;
		}
		return false;
	}

	/**
	 * This method checks to see if the current, possible token is legal in the
	 * defined grammar.
	 *
	 * @return true, if it is a legal token, otherwise false
	 */
	@Override
	public boolean lookupToken() throws CompilerException {
		
		// Get the index of the token from the valid tokens list
		int index = validTokens.indexOf(new Token(currentToken, "none"));
		
		// if there exists a token
		if (index >= 0) {
			// Add the valid token to the Compiler's tokens list
			Compiler.tokens.add(validTokens.get(index));
			currentToken = "";
			return true;
		}
		
		throw new CompilerException("Lexical Error: Invalid Token: " + currentToken);
	}

	// return the current position
	public int getCurrentPosition() {
		return currentPosition;
	}
	
	// return the next character to be analyzed
	public String getNextCharacter() {
		return nextCharacter;
	}
	
	// are there more characters left in the file?
	public boolean moreInFile() {
		return currentPosition < Compiler.file.length();
	}
}
